package com.jintian.smart.kernel.common.expr.format;

import com.jintian.smart.kernel.common.expr.ExpressionToken;
import com.jintian.smart.kernel.common.expr.datameta.BaseDataMeta;
import com.jintian.smart.kernel.common.expr.op.Operator;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

public class ExpressionParser {
    private static Map<String, Operator> operators = new HashMap<String, Operator>(Operator.values().length);

    static {
        for (Operator op : Operator.values()) {
            operators.put(op.getToken(), op);
        }
    }

    /**
     * 通过名称取得操作符
     *
     * @param name
     * @return
     */
    public Operator getOperator(String name) {
        return operators.get(name);
    }

    private Stack<String> parenthesis = new Stack<String>();// 匹配圆括号的栈

    public List<ExpressionToken> getExpressionTokens(String expression) throws FormatException {
        List<ExpressionToken> list = new ArrayList<ExpressionToken>();
        if (expression == null) {
            return list;
        }
        ExpressionReader eReader = new ExpressionReader(expression);
        ExpressionToken expressionToken = null;// 上一次读取的ExpressionToken
        Element ele = null;
        try {
            while ((ele = eReader.readToken()) != null) {

                expressionToken = changeToToken(expressionToken, ele);
                // 如果是括号，则记录下来，最后进行最后进行匹配
                pushParenthesis(ele);
                list.add(expressionToken);
            }
        } catch (Exception e) {
            throw new FormatException("表达式词元格式异常", e);
        } finally {
            eReader.close();
        }
        if (!parenthesis.isEmpty()) {
            throw new FormatException("括号匹配出错:" + expression);
        }

        return list;
    }

    /**
     * 如果是括号，则记录下来，最后进行最后进行匹配
     *
     * @param ele
     */
    private void pushParenthesis(Element ele) throws FormatException {
        if (Element.ElementType.SPLITOR == ele.getType()) {
            if (ele.getText().equals("(")) {
                parenthesis.push("(");
            } else if (ele.getText().equals(")")) {
                if (parenthesis.isEmpty() || !parenthesis.peek().equals("(")) {
                    throw new FormatException("括号匹配出错");
                } else {
                    parenthesis.pop();
                }
            }
        }
    }

    /**
     * 将切分的元素转化成ExpressionToken，并验证减号还是负号
     *
     * @param previousToken
     * @param ele
     * @return
     */
    public ExpressionToken changeToToken(ExpressionToken previousToken, Element ele) {
        if (ele == null) {
            throw new IllegalArgumentException();
        }
        ExpressionToken token = null;

        // 转成ExpressionToken
        if (Element.ElementType.NULL.equals(ele.getType())) {
            token = ExpressionToken.createConstantToken(BaseDataMeta.DataType.DATATYPE_OBJECT, null);
        } else if (Element.ElementType.STRING.equals(ele.getType())) {
            token = ExpressionToken.createConstantToken(BaseDataMeta.DataType.DATATYPE_STRING, ele.getText());
        } else if (Element.ElementType.BOOLEAN.equals(ele.getType())) {
            token = ExpressionToken.createConstantToken(BaseDataMeta.DataType.DATATYPE_BOOLEAN, Boolean.valueOf(ele.getText()));
        } else if (Element.ElementType.INT.equals(ele.getType())) {
            token = ExpressionToken.createConstantToken(BaseDataMeta.DataType.DATATYPE_INT, Integer.valueOf(ele.getText()));
        } else if (Element.ElementType.LONG.equals(ele.getType())) {
            token = ExpressionToken.createConstantToken(BaseDataMeta.DataType.DATATYPE_LONG, Long.valueOf(ele.getText()));
        } else if (Element.ElementType.FLOAT.equals(ele.getType())) {
            token = ExpressionToken.createConstantToken(BaseDataMeta.DataType.DATATYPE_FLOAT, Float.valueOf(ele.getText()));
        } else if (Element.ElementType.DOUBLE.equals(ele.getType())) {
            token = ExpressionToken.createConstantToken(BaseDataMeta.DataType.DATATYPE_DOUBLE, Double.valueOf(ele.getText()));
        } else if (Element.ElementType.BIGDECIMAL.equals(ele.getType())) {
            token = ExpressionToken.createConstantToken(BaseDataMeta.DataType.DATATYPE_BIGDECIMAL, new BigDecimal(ele.getText()));
        } else if (Element.ElementType.DATE.equals(ele.getType())) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            try {
                token = ExpressionToken.createConstantToken(BaseDataMeta.DataType.DATATYPE_DATE, sdf.parse(ele.getText()));
            } catch (Exception e) {
                throw new FormatException("日期格式错误:" + ele.getText());
            }
        } else if (Element.ElementType.VARIABLE.equals(ele.getType())) {
            token = ExpressionToken.createVariableToken(ele.getText());
        } else if (Element.ElementType.OPERATOR.equals(ele.getType())) {
            // 区分负号
            if (ele.getText().equals("-") && (previousToken == null // 以“-”开头肯定是负号
                    || previousToken.getTokenType() == ExpressionToken.ETokenType.ETOKEN_TYPE_OPERATOR // 运算符后面肯定是负号
                    || previousToken.getTokenType() == ExpressionToken.ETokenType.ETOKEN_TYPE_SPLITOR // “(”或“,”后面肯定是负号
                    && !")".equals(previousToken.getSplitor()))) {
                token = ExpressionToken.createOperatorToken(Operator.NG);
            } else {
                token = ExpressionToken.createOperatorToken(getOperator(ele.getText()));
            }
        } else if (Element.ElementType.FUNCTION.equals(ele.getType())) {
            token = ExpressionToken.createFunctionToken(ele.getText());
        } else if (Element.ElementType.SPLITOR.equals(ele.getType())) {
            token = ExpressionToken.createSplitorToken(ele.getText());
        } else if (Element.ElementType.LIST.equals(ele.getType())) {
            token = ExpressionToken.createListToken(ele.getText());
        } else if (Element.ElementType.PROPERTY.equals(ele.getType())) {
            token = ExpressionToken.createPropertyToken(ele.getText());
        }
        token.setStartPosition(ele.getIndex());

        return token;
    }
}
